/*
 * @Author: xiaosihan 
 * @Date: 2024-08-28 09:39:01 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-08-29 14:08:11
 */

import { AdditiveBlending, Box3, BufferAttribute, BufferAttributeJSON, CustomBlending, Group, MeshBasicMaterial, MultiplyBlending, NormalBlending, Plane, Points, PointsMaterial, Ray, SubtractiveBlending, Vector3 } from "three";
import MeshBase from "three-base/MeshBase";
import threeUtils from "three-base/threeUtils";
import pointMapPNG from "./pointMap.png";
import threeLoader from "three-base/threeLoader";
import { Octree } from "three/examples/jsm/math/Octree";
import { round } from "lodash";
import MeshPointsMaterial from "./MeshPointsMaterial";

// 模型粒子效果
export default class MeshParticles extends Group {

    constructor(url?: string) {
        super();
        this.init();
        url && this.mesh.loadModel(url);
    }

    //粒子进度
    particlesProgrees = -0.2;
    setParticlesProgrees(particlesProgrees: number) {
        this.particlesProgrees = particlesProgrees;
        this.clipPlane.constant = this.size.x * particlesProgrees + this.centerPosition.x - (this.size.x / 2);
        this.pointMatril.progress = particlesProgrees;
    }

    // 裁剪面
    clipPlane = (() => {
        const clipPlane = new Plane();
        clipPlane.normal.set(-1, 0, 0);
        return clipPlane;
    })();

    size = new Vector3();
    box = new Box3();
    centerPosition = new Vector3();

    mesh = (() => {
        const mesh = new MeshBase();
        this.add(mesh);
        return mesh;
    })();

    // 粒子的材质
    pointMatril = new MeshPointsMaterial({
        map: threeLoader.getTexture(pointMapPNG)
    });

    // 粒子对象
    particles = (() => {
        const particles = new Points();
        particles.frustumCulled = false;
        particles.material = this.pointMatril;
        (particles.material as PointsMaterial).map = threeLoader.getTexture(pointMapPNG);
        (particles.material as PointsMaterial).transparent = true;
        (particles.material as PointsMaterial).alphaTest = 0.1;
        (particles.material as PointsMaterial).size = 0.5;
        (particles.material as PointsMaterial).blending = NormalBlending;
        this.add(particles);
        return particles;
    })();

    setPointsData(datas: Array<number>) {
        const floatArray = new Float32Array(datas);
        const position = new BufferAttribute(floatArray, 3);
        this.particles.geometry.setAttribute("position", position);
    }

    // 八叉树
    octree = new Octree();

    ray = new Ray();

    init() {

        //加载完成时
        this.mesh.addEventListener("loaded", async () => {
            await this.mesh.awaitLoaded();
            this.mesh.traverseMesh(mesh => {
                (mesh.material as MeshBasicMaterial).clippingPlanes = [this.clipPlane];
            })
            this.box.setFromObject(this.mesh);
            this.size = threeUtils.getSize(this.mesh);
            this.centerPosition = threeUtils.getCenter(this.mesh);
            this.setParticlesProgrees(this.particlesProgrees);
        });
    }

    // 下载粒子数据
    downloadPoinsData() {

        this.octree.fromGraphNode(this.mesh);

        const positionArray: Array<number> = [];

        for (let x = -(this.size.x / 2); x <= (this.size.x / 2); x += 0.05) {
            for (let y = -(this.size.y / 2); y <= (this.size.y / 2); y += 0.05) {
                for (let z = (this.size.z / -2); z <= (this.size.z / 2); z += (this.size.z)) {
                    this.ray.origin.set(x, y, z);
                    this.ray.direction.set(0, 0, -z);
                    const ins = this.octree.rayIntersect(this.ray);
                    if (ins) {
                        positionArray.push(
                            round(ins.position.x, 4),
                            round(ins.position.y, 4),
                            round(ins.position.z + (0.05 * Math.sign(z)), 4)
                        );
                    }
                }
            }
        }

        for (let x = -(this.size.x / 2); x <= (this.size.x / 2); x += 0.05) {
            for (let y = -(this.size.y / 2); y <= (this.size.y / 2); y += (this.size.y)) {
                for (let z = (this.size.z / -2); z <= (this.size.y / 2); z += 0.05) {
                    this.ray.origin.set(x, y, z);
                    this.ray.direction.set(0, -y, 0);
                    const ins = this.octree.rayIntersect(this.ray);
                    if (ins) {
                        positionArray.push(
                            round(ins.position.x, 4),
                            round(ins.position.y + (0.05 * Math.sign(y)), 4),
                            round(ins.position.z, 4)
                        );
                    }
                }
            }
        }

        for (let x = -(this.size.x / 2); x <= (this.size.x / 2); x += (this.size.x)) {
            for (let y = -(this.size.y / 2); y <= (this.size.y / 2); y += 0.05) {
                for (let z = (this.size.z / -2); z <= (this.size.y / 2); z += 0.05) {
                    this.ray.origin.set(x, y, z);
                    this.ray.direction.set(-x, 0, 0);
                    const ins = this.octree.rayIntersect(this.ray);
                    if (ins) {
                        positionArray.push(
                            round(ins.position.x + (0.05 * Math.sign(x)), 4),
                            round(ins.position.y, 4),
                            round(ins.position.z, 4)
                        );
                    }
                }
            }
        }


        const text = JSON.stringify(positionArray);
        const blob = new Blob([text], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = "meshParticles.json";
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }

}